using System;
using ValidationFramework.Configuration.Reflection;

namespace ValidationFramework.Reflection
{
	/// <summary>
	/// A base class for reflected infos (<see cref="System.Reflection.ParameterInfo"/> and <see cref="System.Reflection.PropertyInfo"/>) that are to be cached for performance reasons.
	/// </summary>
	/// <seealso cref="PropertyDescriptor"/>
	/// <seealso cref="ParameterDescriptor"/>
#if (!SILVERLIGHT)
	[Serializable]
#endif
	public abstract class InfoDescriptor : ICachedReflectionInfo
	{

		#region Fields

		/// <summary>
		/// A static instance of typeof(IRuleAttribute).
		/// </summary>
		public static readonly Type RuleAttributeType = typeof(IRuleAttribute);

		#endregion

		#region Constructors

		/// <summary>
		/// Initialize a new instance of <see cref="InfoDescriptor"/>. Exposed for testing purposes.
		/// </summary>
		/// <param name="memberTypeHandle"></param>
		/// <param name="name">The name of the <see cref="InfoDescriptor"/>.</param>
		/// <exception cref="ArgumentNullException"><paramref name="name"/> is null.</exception>
		/// <exception cref="ArgumentException"><paramref name="name"/> is a <see cref="string.Empty"/>.</exception>
		/// <exclude/>
		protected InfoDescriptor(RuntimeTypeHandle memberTypeHandle, string name)
		{
			Guard.ArgumentNotNullOrEmptyString(name, "name");
			Guard.ArgumentNotNull(memberTypeHandle, "memberTypeHandle");
			
			MemberTypeHandle = memberTypeHandle;
			Name = name;

			InitializeMemberRules(memberTypeHandle);

		}

		#endregion

		#region Properties

	    private string name;

	    /// <summary>
		/// Gets the name of the <see cref="InfoDescriptor"/>.
		/// </summary>
		public string Name
	    {
	        get
	        {
	            return name;
	        }
	        private set { name = value; }
	    }

	    /// <summary>
		/// Gets the <see cref="RuntimeTypeHandle"/> for the member this <see cref="InfoDescriptor"/> describes.
		/// </summary>
		/// <remarks>For a <see cref="PropertyDescriptor"/> this will be the <see cref="RuntimeTypeHandle"/> for the return <see cref="Type"/> of the get. For a <see cref="ParameterDescriptor"/> this will be the <see cref="RuntimeTypeHandle"/> for the <see cref="Type"/> of the <see langword="parameter"/>.</remarks>
		public RuntimeTypeHandle MemberTypeHandle
		{
			get;
			private set;
		}


		/// <summary>
		/// Gets or sets the parent scoped rules for this chached member
		/// </summary>
		public RuleCollection ParentScopedRules
		{
			get;
			set;
		}

		/// <summary>
		/// Gets or sets the member scoped rules for this chached member
		/// </summary>
		public RuleCollection MemberScopedRules
		{
			get;
			set;
		}

		/// <summary>
		/// Gets the total number of rules this descriptor containes
		/// </summary>
		public int RuleCount
		{
			get
			{
				return MemberScopedRules.Count + ParentScopedRules.Count;
			}
		}

		#endregion

		#region Methods

		/// <summary>
		/// Get the value for this <see cref="InfoDescriptor"/>.
		/// </summary>
		/// <param name="instance">The object on which to extract the field value. If a <see cref="InfoDescriptor"/> is static, this argument is ignored.</param>
		/// <returns>The value for the <see cref="InfoDescriptor"/>.</returns>
		public abstract object GetValue(object instance);

		public void InitializeMemberRules(RuntimeTypeHandle handle)
		{
			MemberScopedRules = new RuleCollection(handle);
		}

		/// <summary>
		/// Adds a rule to this descriptor and a returns a reference to it
		/// </summary>
		/// <param name="rule"></param>
		/// <param name="scope"></param>
		/// <returns></returns>
        public Rule AddRule(Rule rule, RuleScope scope)
		{
			// assign a rule to the correct rule collection
			if (scope == RuleScope.Parent)
			{
			    return ParentScopedRules.AddNew(rule);
			}
			else
				return MemberScopedRules.AddNew(rule);
		}

        public void ClearRules()
        {
            ParentScopedRules.Clear();
            MemberScopedRules.Clear();
        }
		#endregion

	}
}